package http

import (
	"bytes"
	"card/constant"
	"card/pkg/logger"
	"card/pkg/tconfig"
	"card/pkg/util"
	"context"
	"crypto/tls"
	"encoding/json"
	"github.com/go-resty/resty/v2"
	"io"
	"net/http"
	"net/http/cookiejar"
	"os"
	"time"
)

type Cookie = http.Cookie
type HttpClient struct {
	ctx    context.Context
	client *resty.Client
}

type httpLog struct {
	QueryParams  string
	FormData     string
	Url          string
	RequestTime  string
	Method       string
	Cost         string
	ResponseTime string
	IsSuccess    bool
	Code         string
	Headers      map[string][]string
	LogId        string
	RawBody      interface{}
	Response     string
}

func NewHttpClient(ctx context.Context) *HttpClient {
	cookieJar, _ := cookiejar.New(nil)
	client := resty.New().SetCookieJar(cookieJar)
	// Registering Request Middleware
	client.OnBeforeRequest(func(c *resty.Client, req *resty.Request) error {
		req.Header.Add(constant.LogId, util.GetLogId(ctx))
		return nil
	})

	// Registering Response Middleware
	client.OnAfterResponse(func(c *resty.Client, resp *resty.Response) error {
		isSuccess := resp.IsSuccess()
		log := httpLog{
			QueryParams: resp.Request.QueryParam.Encode(),
			FormData:    resp.Request.FormData.Encode(),
			Url:         resp.Request.URL,
			RequestTime: resp.Request.Time.Format(time.DateTime),
			Method:      resp.Request.Method,
			IsSuccess:   isSuccess,
			Code:        resp.Status(),
			Headers:     resp.Request.Header,
			RawBody:     resp.Request.Body,
			Response:    "",
		}
		if record, _ := resp.Request.Context().Value("log_ignore").(bool); !record {
			log.Response = string(resp.Body())
		}
		bytes, _ := json.Marshal(log)
		msg := "[http]" + string(bytes)
		logger.Info(ctx, msg)
		return nil
	})

	if ctx == nil {
		ctx = context.Background()
	}
	return &HttpClient{
		ctx:    ctx,
		client: client,
	}
}

func (httpClient *HttpClient) Post(path string, service map[string]interface{}, data interface{}, header map[string]string, res interface{}) (*resty.Response, error) {
	if service["ssl"] != nil && service["ssl"].(bool) {
		httpClient.client.SetTLSClientConfig(&tls.Config{
			InsecureSkipVerify: true,
		})
	}
	request := httpClient.client.R().SetContext(httpClient.ctx).SetBody(data)
	url := tconfig.GetString(service["host"].(string)) + path
	return request.SetHeaders(header).SetResult(res).Post(url)
}

func (httpClient *HttpClient) PostFormData(path string, service map[string]interface{}, data map[string]string, header map[string]string, res interface{}) (*resty.Response, error) {
	if service["ssl"] != nil && service["ssl"].(bool) {
		httpClient.client.SetTLSClientConfig(&tls.Config{
			InsecureSkipVerify: true,
		})
	}
	request := httpClient.client.R().SetContext(httpClient.ctx).SetFormData(data)
	url := tconfig.GetString(service["host"].(string)) + path
	return request.SetHeaders(header).SetResult(res).Post(url)
}

func (httpClient *HttpClient) Get(path string, service map[string]interface{}, data map[string]string, header map[string]string, res interface{}) (*resty.Response, error) {
	if service["ssl"] != nil && service["ssl"].(bool) {
		httpClient.client.SetTLSClientConfig(&tls.Config{
			InsecureSkipVerify: true,
		})
	}
	request := httpClient.client.R().SetContext(httpClient.ctx)
	if data != nil {
		request.SetQueryParams(data)
	}
	if header != nil {
		request.SetHeaders(header)
	}
	url := service["host"].(string) + path
	return request.SetResult(res).Get(url)
}

func (httpClient *HttpClient) Upload(path, filePath string, service map[string]interface{}, data map[string]string, header map[string]string, res interface{}) (*resty.Response, error) {
	if service["ssl"] != nil && service["ssl"].(bool) {
		httpClient.client.SetTLSClientConfig(&tls.Config{
			InsecureSkipVerify: true,
		})
	}
	request := httpClient.client.R().SetContext(httpClient.ctx).SetFormData(data)
	url := tconfig.GetString(service["host"].(string)) + path
	return request.SetHeaders(header).SetResult(res).SetFile("file", filePath).SetContentLength(true).Post(url)
}

func (httpClient *HttpClient) DownLoad(url string) ([]byte, error) {
	ctx := context.WithValue(context.Background(), "log_ignore", true)
	resp, err := httpClient.client.R().SetContext(ctx).Get(url)
	if err != nil {
		return nil, err
	}
	return resp.Body(), nil
}

func (httpClient *HttpClient) DownLoadFile(url, path string) error {
	desWriter, err := os.Create(path)
	if err != nil {
		return err
	}
	body, err := httpClient.DownLoad(url)
	if err != nil {
		return err
	}
	if _, err = io.Copy(desWriter, bytes.NewReader(body)); err != nil {
		return err
	}
	return nil
}

// &http.Cookie{ Name:"go-resty", Value:"This is cookie value", Path: "/", Domain: "sample.com", MaxAge: 36000, HttpOnly: true, Secure: false, }
func (httpClient *HttpClient) SetCookies(cookies []*Cookie) *resty.Client {
	return httpClient.client.SetCookies(cookies)
}
